Android 性能优化:StrictMode
    
      
        
        2014.09.07
      
      
        
          
          xiazdong
        
      
      
      
      
       
        
            热度 
           ℃
        
      
      
      
    
  
  
    
      StrictMode
Android 2.3 引入了 android.os.StrictMode(Android 3.0 中又在 StrictMode 中新加入了几个方法),为了能够帮助开发者检测 “主线程” 或 “虚拟机” 的一些影响性能或者不良的代码,为了能够让应用更平滑、响应更快。当然如果你已经足够了解 android 最佳实践,完全可以忽略这个类。注意:这个类只在开发时使用,发布时请去除。
目前 Android 4.0 以上的占有率为 84.3%,因此有很多应用直接 minSdk 设置为 14。因此一般不必考虑 StrictMode 中 API Level 的问题。
StrictMode 共有两种策略(policy):
- ThreadPolicy: 线程相关策略,包括主线程访问网络、磁盘(现在手机中使用闪存)读写、慢代码的检测。我们能够分别检测(detect)这些操作或允许(permit)这些操作。一旦出现了违规(violation),就会有相应的提示(比如 Log 显示)。
- VMPolicy: 虚拟机相关的策略,包括 SQLite 或 SQLiteCursor 没关闭、实现 Closable 接口的类使用后没关闭 等。 
当然也可以忽略某些违规,比如检测磁盘读写,因为一般来说在主线程中进行文件系统的读写是可以的。
一旦我们在入口 Activity 的 onCreate() 的最前面添加 StrictMode 相关代码,则在整个程序中 StrictMode 都会有效。
模版代码:
| protected void onCreate(Bundle savedInstanceState) { 	if (DEVELOPER_MODE) {         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()                     .detectAll()                     .penaltyLog()                     .build());         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()                     .detectLeakedSqlLiteObjects()                     .detectLeakedClosableObjects()                     .penaltyLog()                     .penaltyDeath()                     .build());     }     super.onCreate(savedInstanceState); }
 | 
当然,为了方便,可以直接使用 StrictMode.enableDefaults() 启用 StrictMode。
其实 ThreadPolicy 和 VMPolicy 差不多,detectAll() 表示检测全部的违规,penaltyLog() 表示当违规时打印 Log。
- 对于 ThreadPolicy 来说,我们可以使用 detectNetwork()检测主线程网络访问,detectDiskReads()和detectDiskWrites()检测主线程磁盘读写,detectCustomSlowCalls()检测主线程自定义的慢代码。当然也可以使用permitXXX()允许这些操作。
- 对于 VMPolicy 来说,detectLeakedSqliteObjects()检测 SQLite 和 SQLiteCursor 内存泄漏(没关闭),detectLeakedClosableObjects()检测实现 Closable 接口的对象内存泄漏。
- 我们能通过 StrictMode.getThreadPolicy()和StrictMode.getVMPolicy()获得当前采取的 ThreadPolicy 和 VMPolicy。
案例
场景:从网络上下载一张图片并且在 ImageView 中显示。
核心代码如下:
| ImageView imageView = (ImageView)findViewById(R.id.view);     URL url = null;     Bitmap bmp = null;     try {         url = new URL("http://www.tencent.com/1.jpg");         bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());     }catch(Exception e){         e.printStackTrace();     }finally {         if(bmp!=null){             imageView.setImageBitmap(bmp);         }     }
 | 
这段代码一运行,StrictMode 就出现了一堆建议:
1、 根据下面的 log 可以看出查找域名花费了 446 ms。
| D/StrictMode﹕ StrictMode policy violation; ~duration=446 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=63 violation=4         at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1184)         at java.net.InetAddress.lookupHostByName(InetAddress.java:394)         ...         at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:168)         at info.xiazdong.performanceoptimization.MainActivity.onCreate(MainActivity.java:33)         ...
 | 
2、连接服务器花费 377 ms。
| D/StrictMode﹕ StrictMode policy violation; ~duration=377 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=63 violation=4         at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1184)         at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:84)         at libcore.io.IoBridge.connectErrno(IoBridge.java:127)         at libcore.io.IoBridge.connect(IoBridge.java:112)         ...         at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:168)         at info.xiazdong.performanceoptimization.MainActivity.onCreate(MainActivity.java:36)         ...
 | 
3、 解码图片花费了 205 ms。
| D/StrictMode﹕ StrictMode policy violation; ~duration=205 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=63 violation=4         at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1184)         at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:163)         at libcore.io.IoBridge.recvfrom(IoBridge.java:503)         at java.net.PlainSocketImpl.read(PlainSocketImpl.java:488)         ...         at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:791)         at info.xiazdong.performanceoptimization.MainActivity.onCreate(MainActivity.java:36)         ...
 | 
因此访问网络的操作都建议在子线程中进行,绝对不能在主线程进行。
现在手机的像素越来越高,魅族4的相机像素达到 2070 万,拍出照片的尺寸是 5248  3936,如果这张照片是 ARGB_8888 类型的位图,则放在内存中将占据:2070  4 字节,约 78M,即使手机有足够内存放这张图片,在主线程解码(decode)也需要花费不少时间,因此一般这种耗时的操作都在子线程中进行,比如使用 AsyncTask(因为屏幕像素和相机像素差距比较大,因此一般都不会原图显示,因为手机上显示一张高清图片没有什么价值)
参考文献